﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace LHHP.Window.Controls
{
    public partial class AnimationSelctor : Control
    {
        private Timer timer;
        private int lastIndex;
        private float offsetPercent = 0.1f;
        private float completeOffset = 0f;
        private OffsetDirection direction;
        private BufferedGraphics grafx;
        private bool inAnimation;
        public event EventHandler<SelectIndexChangeEventArgs> SelectIndexChanged;

        RectangleF firstRect;
        RectangleF secondRect;
        RectangleF thirdRect;
        RectangleF fourthRect;
        RectangleF fifthRect;

        private bool isFirstDisplay = true;


        private int currIndex;
        public int CurrentIndex
        {
            get { return currIndex; }
            set
            {
                if (this.ImageList==null || this.ImageList.Count==0)
                {
                    return;
                }
                if (this.inAnimation)
                {
                    return;
                }
                if (this.ImageList.Count == 0)
                {
                    return;
                }
                if (value < 0)
                {
                    value = 0;
                }
                if (value > this.ImageList.Count - 1)
                {
                    value = this.ImageList.Count - 1;
                }
                this.lastIndex = this.currIndex;
                this.currIndex = value;
                if (this.isFirstDisplay)
                {
                    this.Draw();
                    this.isFirstDisplay = false;
                }
                else
                {
                    if (this.currIndex > this.lastIndex)
                    {
                        this.direction = OffsetDirection.Left;
                        this.timer.Start();
                        this.inAnimation = true;
                        this.OnSelectedIndexChanged(this.currIndex);
                    }
                    else if (this.currIndex < this.lastIndex)
                    {
                        this.direction = OffsetDirection.Right;
                        this.timer.Start();
                        this.inAnimation = true;
                        this.OnSelectedIndexChanged(this.currIndex);
                    }
                }

            }
        }

        private List<Image> images;
        public List<Image> ImageList
        {
            get { return images; }
            set { images = value; }
        }

        public AnimationSelctor()
        {
            InitializeComponent();
            this.ImageList = new List<Image>();
            if (!this.DesignMode)
            {
                this.SizeChanged += new EventHandler(AnimationDisplayer_SizeChanged);
                this.Cursor = Cursors.Hand;
                this.timer = new Timer();
                this.timer.Interval = 50;
                this.timer.Tick += new EventHandler(timer_Tick);
            }
        }

        void AnimationDisplayer_SizeChanged(object sender, EventArgs e)
        {
            //14,18,36,18,14
            float height = (float)this.Height;
            float widthStep = (float)this.Width / 100;
            float firstRate = 0.6F;
            float secondRate = 0.8F;
            SizeF thirdSize = new SizeF(widthStep * 36, height);
            SizeF firstSize = new SizeF(thirdSize.Width * firstRate, thirdSize.Height * firstRate);
            SizeF secondSize = new SizeF(thirdSize.Width * secondRate, thirdSize.Height * secondRate);
            this.firstRect = new RectangleF(new PointF(0, (height - firstSize.Height) / 2), firstSize);
            this.secondRect = new RectangleF(new PointF(widthStep * 14, (height - secondSize.Height) / 2), secondSize);
            this.thirdRect = new RectangleF(new PointF(widthStep * 32, 0), thirdSize);
            this.fourthRect = new RectangleF(new PointF(widthStep * 86 - secondSize.Width, (height - secondSize.Height) / 2), secondSize);
            this.fifthRect = new RectangleF(new PointF(this.Width - widthStep * 14, (height - firstSize.Height) / 2), firstSize);

            if (grafx != null)
            {
                grafx.Dispose();
                grafx = null;
            }
            grafx = BufferedGraphicsManager.Current.Allocate(this.CreateGraphics(), new Rectangle(0, 0, this.Width, this.Height));

        }

        void timer_Tick(object sender, EventArgs e)
        {
            completeOffset += offsetPercent;
            if (completeOffset > 1)
            {
                completeOffset = 1;
            }
            this.Move(this.direction);
            if (completeOffset == 1)
            {
                this.completeOffset = 0;
                if (this.currIndex > this.lastIndex + 1)
                {
                    this.lastIndex++;
                }
                else if (this.currIndex < this.lastIndex - 1)
                {
                    this.lastIndex--;
                }
                else
                {
                    this.timer.Stop();
                    this.inAnimation = false;
                }
            }
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
            this.Draw();
            this.isFirstDisplay = false;
        }

        private Rectangle ToRectangle(RectangleF rf)
        {
            return new Rectangle((int)rf.X - 1, (int)rf.Y - 1, (int)rf.Width + 2, (int)rf.Height + 2);
        }

        private void Draw()
        {
            if (this.ImageList == null || this.ImageList.Count == 0)
            {
                return;
            }
            var g = this.grafx.Graphics;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
            g.Clear(this.BackColor);
            var index1 = this.currIndex - 2;
            var index2 = this.currIndex - 1;
            var index4 = this.currIndex + 1;
            var index5 = this.currIndex + 2;
            var imagecount = this.ImageList.Count;
            if (index1 >= 0 && index1 < imagecount)
            {
                g.DrawImage(this.ImageList[index1], firstRect);
            }
            if (index5 >= 0 && index5 < imagecount)
            {
                g.DrawImage(this.ImageList[index5], fifthRect);
            }
            if (index2 >= 0 && index2 <= imagecount)
            {
                g.DrawImage(this.ImageList[index2], secondRect);
            }
            if (index4 >= 0 && index4 < imagecount)
            {
                g.DrawImage(this.ImageList[index4], fourthRect);
            }
            if (this.currIndex >= 0 && this.currIndex < imagecount)
            {
                g.DrawImage(this.ImageList[this.currIndex], thirdRect);
            }
            this.grafx.Render();
        }

        private RectangleF CalculateRectangle(RectangleF source, RectangleF target, float completeOffset)
        {
            RectangleF result = new RectangleF(source.X - (source.X - target.X) * completeOffset, source.Y - (source.Y - target.Y) * completeOffset, source.Width - (source.Width - target.Width) * completeOffset, source.Height - (source.Height - target.Height) * completeOffset);
            return result;
        }

        private void Move(OffsetDirection direction)
        {
            if (this.ImageList==null || this.ImageList.Count==0)
            {
                return;
            }
            var g = this.grafx.Graphics;
            g.Clear(this.BackColor);
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
            RectangleF nfirst, nsecond, nthird, nfourth, nfifth, nzero = RectangleF.Empty, nsix = RectangleF.Empty;
            switch (direction)
            {
                case OffsetDirection.Left:
                    nfirst = CalculateRectangle(firstRect, new RectangleF(0, this.Height / 2, 0, 0), completeOffset);
                    nsecond = CalculateRectangle(secondRect, firstRect, completeOffset);
                    nthird = CalculateRectangle(thirdRect, secondRect, completeOffset);
                    nfourth = CalculateRectangle(fourthRect, thirdRect, completeOffset);
                    nfifth = CalculateRectangle(fifthRect, fourthRect, completeOffset);
                    nsix = CalculateRectangle(new RectangleF(this.Width, this.Height / 2, 0, 0), fifthRect, completeOffset);
                    break;
                case OffsetDirection.Right:
                    nfirst = CalculateRectangle(firstRect, secondRect, completeOffset);
                    nsecond = CalculateRectangle(secondRect, thirdRect, completeOffset);
                    nthird = CalculateRectangle(thirdRect, fourthRect, completeOffset);
                    nfourth = CalculateRectangle(fourthRect, fifthRect, completeOffset);
                    nfifth = CalculateRectangle(fifthRect, new RectangleF(this.Width, this.Height / 2, 0, 0), completeOffset);
                    nzero = CalculateRectangle(new RectangleF(0, this.Height / 2, 0, 0), firstRect, completeOffset);
                    break;
                default:
                    nfirst = new RectangleF(firstRect.Location, firstRect.Size);
                    nsecond = new RectangleF(secondRect.Location, secondRect.Size);
                    nthird = new RectangleF(thirdRect.Location, thirdRect.Size);
                    nfourth = new RectangleF(fourthRect.Location, fourthRect.Size);
                    nfifth = new RectangleF(fifthRect.Location, fifthRect.Size);
                    break;
            }
            int index3 = this.lastIndex;
            var imagecount = this.ImageList.Count;
            var index1 = index3 - 2;
            var index2 = index3 - 1;
            var index4 = index3 + 1;
            var index5 = index3 + 2;
            if (direction == OffsetDirection.Left)
            {
                if (index1 >= 0 && index1 < imagecount)
                {
                    g.DrawImage(this.ImageList[index1], nfirst);
                }
                var index6 = index3 + 3;
                if (index6 >= 0 && index6 < imagecount)
                {
                    if (nsix != RectangleF.Empty)
                    {
                        g.DrawImage(this.ImageList[index6], nsix);
                    }
                }
                if (index5 >= 0 && index5 < imagecount)
                {
                    g.DrawImage(this.ImageList[index5], nfifth);
                }
                if (index2 >= 0 && index2 <= imagecount)
                {
                    g.DrawImage(this.ImageList[index2], nsecond);
                }

                if (this.currIndex >= 0 && this.currIndex < imagecount)
                {
                    g.DrawImage(this.ImageList[index3], nthird);
                }
                if (index4 >= 0 && index4 < imagecount)
                {
                    g.DrawImage(this.ImageList[index4], nfourth);
                }
            }
            else
            {
                var index0 = index3 - 3;
                if (index0 >= 0 && index0 < imagecount)
                {
                    if (nzero != RectangleF.Empty)
                    {
                        g.DrawImage(this.ImageList[index0], nzero);
                    }
                }

                if (index5 >= 0 && index5 < imagecount)
                {
                    g.DrawImage(this.ImageList[index5], nfifth);
                }
                if (index1 >= 0 && index1 < imagecount)
                {
                    g.DrawImage(this.ImageList[index1], nfirst);
                }
                if (index4 >= 0 && index4 < imagecount)
                {
                    g.DrawImage(this.ImageList[index4], nfourth);
                }
                if (this.currIndex >= 0 && this.currIndex < imagecount)
                {
                    g.DrawImage(this.ImageList[index3], nthird);
                }
                if (index2 >= 0 && index2 <= imagecount)
                {
                    g.DrawImage(this.ImageList[index2], nsecond);
                }
            }
            this.grafx.Render();
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            if (!this.inAnimation)
            {
                var index = this.IndexOfPosition(e.X, e.Y);
                this.CurrentIndex = index;
            }
        }

        protected void OnSelectedIndexChanged(int index)
        {
            if (this.SelectIndexChanged != null)
            {
                this.SelectIndexChanged(this, new SelectIndexChangeEventArgs(index));
            }
        }

        private int IndexOfPosition(int x, int y)
        {
            float widthStep = (float)this.Width / 100;
            if (x >= 0 && x < widthStep * 14)
            {
                return this.CurrentIndex - 2;
            }
            else if (x >= widthStep * 14 && x < widthStep * 32)
            {
                return this.CurrentIndex - 1;
            }
            else if (x >= widthStep * 32 && x < widthStep * 68)
            {
                return this.CurrentIndex;
            }
            else if (x >= widthStep * 68 && x < widthStep * 86)
            {
                return this.CurrentIndex + 1;
            }
            else
            {
                return this.CurrentIndex + 2;
            }
        }


        protected override void DestroyHandle()
        {
            base.DestroyHandle();
            if (this.ImageList != null)
            {
                foreach (Image bmp in this.ImageList)
                {
                    bmp.Dispose();
                }
            }
            if (this.timer != null)
            {
                this.timer.Dispose();
            }
            if (grafx != null)
            {
                this.grafx.Dispose();
            }
        }

        private enum OffsetDirection
        {
            Left,
            Right
        }

        public class SelectIndexChangeEventArgs : EventArgs
        {
            public int SelectedIndex { get; set; }

            public SelectIndexChangeEventArgs(int index)
            {
                this.SelectedIndex = index;
            }
        }
    }
}
